package org.jordens.jdbcspy;

import java.io.*;
import java.sql.*;
import java.util.*;

/**
 * The JDBCSpyDriver wraps another JDBCDriver and creates a JDBCSpyConnection
 * that in turn provides statistcs based on Statement and PreparedStatement usage.
 *
 * There are two ways to specify what JDBC driver class should be wrapped:
 * - Set the 'JDBCSpyDriver.resetInterval' equal to the fully qualified class name.
 * - Ensure that the JDBC driver has been loaded previously (ie. Class.forName(xxx.yyy.zzz);)
 *
 * It's also possible to specify parameters to the JDBCSpyDriver as either System properties
 * or as entries in a 'jdbcspy.properties' file that resides on the applications classpath.
 *
 * Valid Parameters:
 * - JDBCspyDriver.wrappedDriver - specifies what JDBCSpy should actually be wrapped
 * - JDBCSpyDriver.resetInterval - determines how frequently JDBC statistics will
 * be automatically reset. Default is 60000 or the equivalent of 60s.
 * - JDBCSpyDriver.logFile - statistics will be written to the specified file. Disabled
 * if parameter not specified.
 *
 * @author Adam Jordens
 */
public class JDBCSpyDriver implements Driver
{
    private static final JDBCSpyProperties jdbcSpyProperties = JDBCSpyProperties.getInstance();

    private static final String WRAPPED_CLASS = jdbcSpyProperties.getWrappedDriver();

    private final Driver wrappedDriver;

    static
    {
        try
        {
            JDBCSpyDriver driver = new JDBCSpyDriver();
            DriverManager.registerDriver(driver);
            System.out.println("JDBCSpyDriver Driver Registered [" + driver.wrappedDriver + "]");
        }
        catch (SQLException e)
        {
            throw new IllegalStateException(e);
        }
    }

    /**
     * Constructs a new JDBCSpyDriver.
     */
    public JDBCSpyDriver()
    {
        try
        {
            if (WRAPPED_CLASS != null)
            {
                Class clazz = Class.forName(WRAPPED_CLASS);

                // Specific JDBC driver specified, use first driver matching class
                Enumeration e = DriverManager.getDrivers();
                while (e.hasMoreElements())
                {
                    Driver d = (Driver)e.nextElement();
                    if (clazz.equals(d.getClass()))
                    {
                        wrappedDriver = d;
                        return;
                    }
                }
            }
            else
            {
                // No specific JDBC driver specified, use first available.
                Enumeration e = DriverManager.getDrivers();
                if (e.hasMoreElements())
                {
                    wrappedDriver = (Driver)e.nextElement();
                    return;
                }
            }

            throw new IllegalStateException("No available JDBC drivers to wrap");
        }
        catch (ClassNotFoundException e)
        {
            throw new IllegalStateException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public Connection connect(String s, Properties properties) throws SQLException
    {
        if (s.startsWith("spy:"))
        {
            String url = s.replaceAll("spy:", "");
            Connection wrappedConnection = DriverManager.getConnection(url, properties);

            JDBCSpyConnection conn = new JDBCSpyConnection(wrappedConnection);

            Long resetInterval = jdbcSpyProperties.getResetInterval();
            if (resetInterval != null)
            {
                conn.getJDBCSpy().setResetInterval(resetInterval);
            }

            File logFile = jdbcSpyProperties.getLogFile();
            if (logFile != null)
            {
                conn.getJDBCSpy().setLogFile(logFile);
            }

            return conn;
        }

        return wrappedDriver.connect(s, properties);
    }

    /**
     * {@inheritDoc}
     */
    public boolean acceptsURL(String s) throws SQLException
    {
        return wrappedDriver.acceptsURL(s.replaceAll("spy:", ""));
    }

    /**
     * {@inheritDoc}
     */
    public DriverPropertyInfo[] getPropertyInfo(String s, Properties properties) throws SQLException
    {
        return wrappedDriver.getPropertyInfo(s, properties);
    }                                                      

    /**
     * {@inheritDoc}
     */
    public int getMajorVersion()
    {
        return wrappedDriver.getMajorVersion();
    }

    /**
     * {@inheritDoc}
     */
    public int getMinorVersion()
    {
        return wrappedDriver.getMinorVersion();
    }

    /**
     * {@inheritDoc}
     */
    public boolean jdbcCompliant()
    {
        return wrappedDriver.jdbcCompliant();
    }
}
